策略模式(Strategy Pattern)
定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
设计原则
- 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
- 针对接口编程,而不是针对实现编程
- 多用组合,少用继承
模拟鸭子游戏
有一款模拟鸭子游戏,游戏中会出现各种鸭子,一边游泳,一边呱呱叫。根据面向对象技术,设计了一个鸭子的超类,让各种鸭子继承这个超类。

1 | public abstract class Duck { |
1 | class MallardDuck extends Duck { |
1 | public class RedheadDuck extends Duck { |
测试代码:
1 | public class Main { |
运行结果:
外观是绿头
呱呱叫
游泳
外观是红头
呱呱叫
游泳
让鸭子会飞
某一天,增加了一个需求,需要让鸭子会飞。于是在 Duck 类中增加了一个 fly() 方法。

可怕的问题来了,某天增加了一种橡皮鸭(不会飞、会吱吱叫)、一种诱饵鸭(不会飞、不会叫)。
由于继承的原因,橡皮鸭、诱饵鸭都变成了会飞、会呱呱叫。解决办法就是在橡皮鸭、诱饵鸭类中覆盖 fly() 和 quack() 方法。


1 | public abstract class Duck { |
1 | public class RubberDuck extends Duck { |
1 | public class DecoyDuck extends Duck{ |
测试代码:
1 | public class Main { |
运行结果:
外观是绿头
呱呱叫
游泳
飞
外观是红头
呱呱叫
游泳
飞
外观是橡皮鸭
吱吱叫
游泳
不会飞
外观是诱饵鸭
不会叫
游泳
不会飞
利用继承来提供Duck的行为,会导致以下问题:
- 代码在多个子类中重复
- 运行时的行为不容易改变
- 很难知道所有鸭子的全部行为
- 改变会牵一发动全身,造成其他鸭子不想要的改变
利用接口实现
把 fly() 从超类中提取出来,放进一个 Flyable 接口,这么一来,只有会飞的鸭子才实现。同样也增加一个 Quackable 接口,因为有的鸭子不会叫。

1 | public abstract class Duck { |
1 | public interface Flyable { |
1 | public interface Quackable { |
1 | public class MallardDuck extends Duck implements Flyable,Quackable { |
1 | public class RedheadDuck extends Duck implements Flyable,Quackable { |
1 | public class RubberDuck extends Duck implements Quackable { |
1 | public class DecoyDuck extends Duck { |
测试代码:
1 | /** |
运行结果:
外观是绿头
呱呱叫
游泳
飞
外观是红头
呱呱叫
游泳
飞
外观是橡皮鸭
吱吱叫
游泳
外观是诱饵鸭
游泳
我们知道,并非所有的子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方法。虽然利用 Flyable 和 Quackable 接口可以解决一部分问题,但是会造成代码无法复用,重复代码变多(假如有48种鸭子,每种鸭子都要去实现 fly() 和 quack() 方法)。
分开变化和不会变化的部分
将鸭子会变化的行为 fly 和 quack 放在分开的类中,此类专门提供某行为接口的实现。

这样的设计,飞行和呱呱叫的行为已经和鸭子无关,可以被其他对象复用。
整合鸭子的行为
在鸭子类中加入两个实例变量 flyBehavior 、quackBehavior,声明为接口类型。
每个鸭子都会动态地设置这两个变量以在运行时引用正确的行为类型(FlyWithWings 、Squeak 等)

定义行为:1
2
3public interface FlyBehavior {
public void fly();
}
1 | public class FlyWithWings implements FlyBehavior { |
1 | public class FlyNoWay implements FlyBehavior { |
1 | public interface QuackBehavior { |
1 | public class Quack implements QuackBehavior { |
1 | public class Squeak implements QuackBehavior { |
1 | public class MuteQuack implements QuackBehavior { |
鸭子类:
1 | public abstract class Duck { |
绿头鸭实现类:
1 | public class MallardDuck extends Duck { |
测试代码:
1 | public class TestMallardDuck { |
运行结果:
外观是绿头
游泳
飞
呱呱叫
动态设定行为
创建一个模型鸭,一开始是不会飞的,后面增加一个火箭飞行的行为,让模型鸭可以飞。
1 | public class ModelDuck extends Duck { |
1 | public class FlyRocketPowered implements FlyBehavior { |
测试代码:
1 | public class TestModelDuck { |
运行结果:
外观是模型鸭
游泳
不会飞
呱呱叫
火箭飞行
总结
多用组合,少用继承。
使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态改变行为。